// Copyright (c) 2009-2011, Tor M. Aamodt, Wilson W.L. Fung, Ali Bakhoda,
// Ivan Sham, George L. Yuan,
// The University of British Columbia
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution. Neither the name of
// The University of British Columbia nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include "dram.h"
#include "dram_sched.h"
#include "hashing.h"
#include "l2cache.h"
#include "simulator_sim.h"

#ifdef DRAM_VERIFY
int PRINT_CYCLE = 0;
#endif

template class FifoPipeline<MemFetch>;
template class FifoPipeline<DramReq>;

Dram::Dram(uint32_t partition_id, const MemoryConfig *config,
           MemoryPartitionUnit *mp,
           SIMULATOR *simulator)
{
  id = partition_id;
  m_memory_partition_unit = mp;
  m_config = config;
  m_simulator = simulator;

  // rowblp
  access_num = 0;
  hits_num = 0;
  read_num = 0;
  write_num = 0;
  hits_read_num = 0;
  hits_write_num = 0;
  banks_1time = 0;
  banks_acess_total = 0;
  banks_acess_total_after = 0;
  banks_time_ready = 0;
  banks_access_ready_total = 0;
  issued_two = 0;
  issued_total = 0;
  issued_total_row = 0;
  issued_total_col = 0;

  CCDc = 0;
  RRDc = 0;
  RTWc = 0;
  WTRc = 0;

  wasted_bw_row = 0;
  wasted_bw_col = 0;
  util_bw = 0;
  idle_bw = 0;
  RCDc_limit = 0;
  CCDLc_limit = 0;
  CCDLc_limit_alone = 0;
  CCDc_limit = 0;
  WTRc_limit = 0;
  WTRc_limit_alone = 0;
  RCDWRc_limit = 0;
  RTWc_limit = 0;
  RTWc_limit_alone = 0;
  rwq_limit = 0;
  write_to_read_ratio_blp_rw_average = 0;
  bkgrp_parallsim_rw = 0;

  rw = READ; // read mode is default

  bkgrp = (BankGrp **)calloc(sizeof(BankGrp *), m_config->nbkgrp);
  bkgrp[0] = (BankGrp *)calloc(sizeof(Bank), m_config->nbkgrp);
  for (uint32_t i = 1; i < m_config->nbkgrp; i++)
  {
    bkgrp[i] = bkgrp[0] + i;
  }
  for (uint32_t i = 0; i < m_config->nbkgrp; i++)
  {
    bkgrp[i]->CCDLc = 0;
    bkgrp[i]->RTPLc = 0;
  }

  bk = (Bank **)calloc(sizeof(Bank *), m_config->nbk);
  bk[0] = (Bank *)calloc(sizeof(Bank), m_config->nbk);
  for (uint32_t i = 1; i < m_config->nbk; i++)
    bk[i] = bk[0] + i;
  for (uint32_t i = 0; i < m_config->nbk; i++)
  {
    bk[i]->state = BANK_IDLE;
    bk[i]->bkgrpindex = i / (m_config->nbk / m_config->nbkgrp);
  }
  prio = 0;

  rwq = new FifoPipeline<DramReq>("rwq", m_config->CL, m_config->CL + 1);
  mrqq = new FifoPipeline<DramReq>("mrqq", 0, 2);
  returnq = new FifoPipeline<MemFetch>(
      "dramreturnq", 0,
      m_config->simulator_dram_return_queue_size == 0
          ? 1024
          : m_config->simulator_dram_return_queue_size);
  m_frfcfs_scheduler = NULL;
  if (m_config->scheduler_type == DRAM_FRFCFS)
    m_frfcfs_scheduler = new FrfcfsScheduler(m_config, this);
  n_cmd = 0;
  n_activity = 0;
  n_nop = 0;
  n_act = 0;
  n_pre = 0;
  n_rd = 0;
  n_wr = 0;
  n_wr_WB = 0;
  n_rd_L2_A = 0;
  n_req = 0;
  max_mrqs_temp = 0;
  bwutil = 0;
  max_mrqs = 0;
  ave_mrqs = 0;

  for (uint32_t i = 0; i < 10; i++)
  {
    dram_util_bins[i] = 0;
    dram_eff_bins[i] = 0;
  }
  last_n_cmd = last_n_activity = last_bwutil = 0;

  n_cmd_partial = 0;
  n_activity_partial = 0;
  n_nop_partial = 0;
  n_act_partial = 0;
  n_pre_partial = 0;
  n_req_partial = 0;
  ave_mrqs_partial = 0;
  bwutil_partial = 0;

  if (queue_limit())
    mrqq_Dist = StatCreate("mrqq_length", 1, queue_limit());
  else                                            // queue length is unlimited;
    mrqq_Dist = StatCreate("mrqq_length", 1, 64); // track up to 64 entries
}

bool Dram::full(bool is_write) const
{
  if (m_config->scheduler_type == DRAM_FRFCFS)
  {
    if (m_config->simulator_frfcfs_dram_sched_queue_size == 0)
      return false;
    if (m_config->seperate_write_queue_enabled)
    {
      if (is_write)
        return m_frfcfs_scheduler->num_write_pending() >=
               m_config->simulator_frfcfs_dram_write_queue_size;
      else
        return m_frfcfs_scheduler->num_pending() >=
               m_config->simulator_frfcfs_dram_sched_queue_size;
    }
    else
      return m_frfcfs_scheduler->num_pending() >=
             m_config->simulator_frfcfs_dram_sched_queue_size;
  }
  else
    return mrqq->full();
}

uint32_t Dram::que_length() const
{
  uint32_t nreqs = 0;
  if (m_config->scheduler_type == DRAM_FRFCFS)
  {
    nreqs = m_frfcfs_scheduler->num_pending();
  }
  else
  {
    nreqs = mrqq->get_length();
  }
  return nreqs;
}

bool Dram::returnq_full() const { return returnq->full(); }

uint32_t Dram::queue_limit() const
{
  return m_config->simulator_frfcfs_dram_sched_queue_size;
}

DramReq::DramReq(class MemFetch *mf, uint32_t banks,
                 uint32_t dram_bnk_indexing_policy,
                 class SIMULATOR *simulator)
{
  txbytes = 0;
  dqbytes = 0;
  data = mf;
  m_simulator = simulator;

  const addrdec_t &tlx = mf->get_tlx_addr();

  switch (dram_bnk_indexing_policy)
  {
  case LINEAR_BK_INDEX:
  {
    bk = tlx.bk;
    break;
  }
  case BITWISE_XORING_BK_INDEX:
  {
    // xoring bank bits with lower bits of the page
    bk = bitwise_hash_function(tlx.row, tlx.bk, banks);
    assert(bk < banks);
    break;
  }
  case IPOLY_BK_INDEX:
  {
    /*IPOLY for bank indexing function from "Pseudo-randomly interleaved
     * memory." Rau, B. R et al. ISCA 1991
     * http://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=348DEA37A3E440473B3C075EAABC63B6?doi=10.1.1.12.7149&rep=rep1&type=pdf
     */
    // xoring bank bits with lower bits of the page
    bk = ipoly_hash_function(tlx.row, tlx.bk, banks);
    assert(bk < banks);
    break;
  }
  case CUSTOM_BK_INDEX:
    /* No custom set function implemented */
    // Do you custom index here
    break;
  default:
    assert("\nUndefined bank index function.\n" && 0);
    break;
  }

  row = tlx.row;
  col = tlx.col;
  nbytes = mf->get_data_size();

  timestamp = m_simulator->simulator_sim_cycle;
  addr = mf->get_addr();
  insertion_time = (uint32_t)simulator->simulator_sim_cycle;
  rw = data->get_is_write() ? WRITE : READ;
}

void Dram::push(class MemFetch *data)
{
  assert(id == data->get_tlx_addr()
                   .chip); // Ensure request is in correct memory partition

  DramReq *mrq =
      new DramReq(data, m_config->nbk, m_config->dram_bnk_indexing_policy,
                  m_memory_partition_unit->get_simulator());

  data->set_status(IN_PARTITION_MC_INTERFACE_QUEUE,
                   m_simulator->simulator_sim_cycle);
  mrqq->push(mrq);

  // stats...
  n_req += 1;
  n_req_partial += 1;
  if (m_config->scheduler_type == DRAM_FRFCFS)
  {
    uint32_t nreqs = m_frfcfs_scheduler->num_pending();
    if (nreqs > max_mrqs_temp)
      max_mrqs_temp = nreqs;
  }
  else
  {
    max_mrqs_temp = (max_mrqs_temp > mrqq->get_length()) ? max_mrqs_temp
                                                         : mrqq->get_length();
  }
  // m_stats->memlatstat_dram_access(data);
}

void Dram::scheduler_fifo()
{
  if (!mrqq->empty())
  {
    uint32_t bkn;
    DramReq *head_mrqq = mrqq->top();
    head_mrqq->data->set_status(
        IN_PARTITION_MC_BANK_ARB_QUEUE,
        m_simulator->simulator_sim_cycle);
    bkn = head_mrqq->bk;
    if (!bk[bkn]->mrq)
      bk[bkn]->mrq = mrqq->pop();
  }
}

#define DEC2ZERO(x) x = (x) ? (x - 1) : 0;
#define SWAP(a, b) \
  a ^= b;          \
  b ^= a;          \
  a ^= b;

void Dram::cycle()
{
  if (!returnq->full())
  {
    DramReq *cmd = rwq->pop();
    if (cmd)
    {
#ifdef DRAM_VIEWCMD
      printf("\tDQ: BK%d Row:%03x Col:%03x", cmd->bk, cmd->row,
             cmd->col + cmd->dqbytes);
#endif
      cmd->dqbytes += m_config->dram_atom_size;

      if (cmd->dqbytes >= cmd->nbytes)
      {
        MemFetch *data = cmd->data;
        data->set_status(IN_PARTITION_MC_RETURNQ,
                         m_simulator->simulator_sim_cycle);
        if (data->get_access_type() != L1_WRBK_ACC &&
            data->get_access_type() != L2_WRBK_ACC)
        {
          data->set_reply();
          returnq->push(data);
        }
        else
        {
          m_memory_partition_unit->set_done(data);
          delete data;
        }
        delete cmd;
      }
#ifdef DRAM_VIEWCMD
      printf("\n");
#endif
    }
  }

  /* check if the upcoming request is on an idle bank */
  /* Should we modify this so that multiple requests are checked? */

  switch (m_config->scheduler_type)
  {
  case DRAM_FIFO:
    scheduler_fifo();
    break;
  case DRAM_FRFCFS:
    scheduler_frfcfs();
    break;
  default:
    printf("Error: Unknown DRAM scheduler type\n");
    assert(0);
  }
  if (m_config->scheduler_type == DRAM_FRFCFS)
  {
    uint32_t nreqs = m_frfcfs_scheduler->num_pending();
    if (nreqs > max_mrqs)
    {
      max_mrqs = nreqs;
    }
    ave_mrqs += nreqs;
    ave_mrqs_partial += nreqs;
  }
  else
  {
    if (mrqq->get_length() > max_mrqs)
    {
      max_mrqs = mrqq->get_length();
    }
    ave_mrqs += mrqq->get_length();
    ave_mrqs_partial += mrqq->get_length();
  }

  uint32_t k = m_config->nbk;
  bool issued = false;

  // collect row buffer locality, BLP and other statistics
  /////////////////////////////////////////////////////////////////////////
  uint32_t memory_pending = 0;
  for (uint32_t i = 0; i < m_config->nbk; i++)
  {
    if (bk[i]->mrq)
      memory_pending++;
  }
  banks_1time += memory_pending;
  if (memory_pending > 0)
    banks_acess_total++;

  uint32_t memory_pending_rw = 0;
  uint32_t read_blp_rw = 0;
  uint32_t write_blp_rw = 0;
  std::bitset<8> bnkgrp_rw_found; // assume max we have 8 bank groups

  for (uint32_t j = 0; j < m_config->nbk; j++)
  {
    uint32_t grp = get_bankgrp_number(j);
    if (bk[j]->mrq &&
        (((bk[j]->curr_row == bk[j]->mrq->row) && (bk[j]->mrq->rw == READ) &&
          (bk[j]->state == BANK_ACTIVE))))
    {
      memory_pending_rw++;
      read_blp_rw++;
      bnkgrp_rw_found.set(grp);
    }
    else if (bk[j]->mrq &&
             (((bk[j]->curr_row == bk[j]->mrq->row) &&
               (bk[j]->mrq->rw == WRITE) && (bk[j]->state == BANK_ACTIVE))))
    {
      memory_pending_rw++;
      write_blp_rw++;
      bnkgrp_rw_found.set(grp);
    }
  }
  banks_time_rw += memory_pending_rw;
  bkgrp_parallsim_rw += bnkgrp_rw_found.count();
  if (memory_pending_rw > 0)
  {
    write_to_read_ratio_blp_rw_average +=
        (double)write_blp_rw / (write_blp_rw + read_blp_rw);
    banks_access_rw_total++;
  }

  uint32_t memory_Pending_ready = 0;
  for (uint32_t j = 0; j < m_config->nbk; j++)
  {
    uint32_t grp = get_bankgrp_number(j);
    if (bk[j]->mrq &&
        ((!CCDc && !bk[j]->RCDc && !(bkgrp[grp]->CCDLc) &&
          (bk[j]->curr_row == bk[j]->mrq->row) && (bk[j]->mrq->rw == READ) &&
          (WTRc == 0) && (bk[j]->state == BANK_ACTIVE) && !rwq->full()) ||
         (!CCDc && !bk[j]->RCDWRc && !(bkgrp[grp]->CCDLc) &&
          (bk[j]->curr_row == bk[j]->mrq->row) && (bk[j]->mrq->rw == WRITE) &&
          (RTWc == 0) && (bk[j]->state == BANK_ACTIVE) && !rwq->full())))
    {
      memory_Pending_ready++;
    }
  }
  banks_time_ready += memory_Pending_ready;
  if (memory_Pending_ready > 0)
    banks_access_ready_total++;
  ///////////////////////////////////////////////////////////////////////////////////

  bool issued_col_cmd = false;
  bool issued_row_cmd = false;

  if (m_config->dual_bus_interface)
  {
    // dual bus interface
    // issue one row command and one column command
    for (uint32_t i = 0; i < m_config->nbk; i++)
    {
      uint32_t j = (i + prio) % m_config->nbk;
      issued_col_cmd = issue_col_command(j);
      if (issued_col_cmd)
        break;
    }
    for (uint32_t i = 0; i < m_config->nbk; i++)
    {
      uint32_t j = (i + prio) % m_config->nbk;
      issued_row_cmd = issue_row_command(j);
      if (issued_row_cmd)
        break;
    }
    for (uint32_t i = 0; i < m_config->nbk; i++)
    {
      uint32_t j = (i + prio) % m_config->nbk;
      if (!bk[j]->mrq)
      {
        if (!CCDc && !RRDc && !RTWc && !WTRc && !bk[j]->RCDc && !bk[j]->RASc &&
            !bk[j]->RCc && !bk[j]->RPc && !bk[j]->RCDWRc)
          k--;
        bk[j]->n_idle++;
      }
    }
  }
  else
  {
    // single bus interface
    // issue only one row/column command
    for (uint32_t i = 0; i < m_config->nbk; i++)
    {
      uint32_t j = (i + prio) % m_config->nbk;
      if (!issued_col_cmd)
        issued_col_cmd = issue_col_command(j);

      if (!issued_col_cmd && !issued_row_cmd)
        issued_row_cmd = issue_row_command(j);

      if (!bk[j]->mrq)
      {
        if (!CCDc && !RRDc && !RTWc && !WTRc && !bk[j]->RCDc && !bk[j]->RASc &&
            !bk[j]->RCc && !bk[j]->RPc && !bk[j]->RCDWRc)
          k--;
        bk[j]->n_idle++;
      }
    }
  }

  issued = issued_row_cmd || issued_col_cmd;
  if (!issued)
  {
    n_nop++;
    n_nop_partial++;
#ifdef DRAM_VIEWCMD
    printf("\tNOP                        ");
#endif
  }
  if (k)
  {
    n_activity++;
    n_activity_partial++;
  }
  n_cmd++;
  n_cmd_partial++;
  if (issued)
  {
    issued_total++;
    if (issued_col_cmd && issued_row_cmd)
      issued_two++;
  }
  if (issued_col_cmd)
    issued_total_col++;
  if (issued_row_cmd)
    issued_total_row++;

  // Collect some statistics
  // check the limitation, see where BW is wasted?
  /////////////////////////////////////////////////////////
  uint32_t memory_pending_found = 0;
  for (uint32_t i = 0; i < m_config->nbk; i++)
  {
    if (bk[i]->mrq)
      memory_pending_found++;
  }
  if (memory_pending_found > 0)
    banks_acess_total_after++;

  bool memory_pending_rw_found = false;
  for (uint32_t j = 0; j < m_config->nbk; j++)
  {
    if (bk[j]->mrq &&
        (((bk[j]->curr_row == bk[j]->mrq->row) && (bk[j]->mrq->rw == READ) &&
          (bk[j]->state == BANK_ACTIVE)) ||
         ((bk[j]->curr_row == bk[j]->mrq->row) && (bk[j]->mrq->rw == WRITE) &&
          (bk[j]->state == BANK_ACTIVE))))
      memory_pending_rw_found = true;
  }

  if (issued_col_cmd || CCDc)
    util_bw++;
  else if (memory_pending_rw_found)
  {
    wasted_bw_col++;
    for (uint32_t j = 0; j < m_config->nbk; j++)
    {
      uint32_t grp = get_bankgrp_number(j);
      // read
      if (bk[j]->mrq &&
          (((bk[j]->curr_row == bk[j]->mrq->row) && (bk[j]->mrq->rw == READ) &&
            (bk[j]->state == BANK_ACTIVE))))
      {
        if (bk[j]->RCDc)
          RCDc_limit++;
        if (bkgrp[grp]->CCDLc)
          CCDLc_limit++;
        if (WTRc)
          WTRc_limit++;
        if (CCDc)
          CCDc_limit++;
        if (rwq->full())
          rwq_limit++;
        if (bkgrp[grp]->CCDLc && !WTRc)
          CCDLc_limit_alone++;
        if (!bkgrp[grp]->CCDLc && WTRc)
          WTRc_limit_alone++;
      }
      // write
      else if (bk[j]->mrq &&
               ((bk[j]->curr_row == bk[j]->mrq->row) &&
                (bk[j]->mrq->rw == WRITE) && (bk[j]->state == BANK_ACTIVE)))
      {
        if (bk[j]->RCDWRc)
          RCDWRc_limit++;
        if (bkgrp[grp]->CCDLc)
          CCDLc_limit++;
        if (RTWc)
          RTWc_limit++;
        if (CCDc)
          CCDc_limit++;
        if (rwq->full())
          rwq_limit++;
        if (bkgrp[grp]->CCDLc && !RTWc)
          CCDLc_limit_alone++;
        if (!bkgrp[grp]->CCDLc && RTWc)
          RTWc_limit_alone++;
      }
    }
  }
  else if (memory_pending_found)
    wasted_bw_row++;
  else if (!memory_pending_found)
    idle_bw++;
  else
    assert(1);

  /////////////////////////////////////////////////////////

  // decrements counters once for each time dram_issueCMD is called
  DEC2ZERO(RRDc);
  DEC2ZERO(CCDc);
  DEC2ZERO(RTWc);
  DEC2ZERO(WTRc);
  for (uint32_t j = 0; j < m_config->nbk; j++)
  {
    DEC2ZERO(bk[j]->RCDc);
    DEC2ZERO(bk[j]->RASc);
    DEC2ZERO(bk[j]->RCc);
    DEC2ZERO(bk[j]->RPc);
    DEC2ZERO(bk[j]->RCDWRc);
    DEC2ZERO(bk[j]->WTPc);
    DEC2ZERO(bk[j]->RTPc);
  }
  for (uint32_t j = 0; j < m_config->nbkgrp; j++)
  {
    DEC2ZERO(bkgrp[j]->CCDLc);
    DEC2ZERO(bkgrp[j]->RTPLc);
  }

#ifdef DRAM_VISUALIZE
  visualize();
#endif
}

bool Dram::issue_col_command(int j)
{
  bool issued = false;
  uint32_t grp = get_bankgrp_number(j);
  if (bk[j]->mrq)
  { // if currently servicing a memory request
    bk[j]->mrq->data->set_status(
        IN_PARTITION_DRAM, m_simulator->simulator_sim_cycle);
    // correct row activated for a READ
    if (!issued && !CCDc && !bk[j]->RCDc && !(bkgrp[grp]->CCDLc) &&
        (bk[j]->curr_row == bk[j]->mrq->row) && (bk[j]->mrq->rw == READ) &&
        (WTRc == 0) && (bk[j]->state == BANK_ACTIVE) && !rwq->full())
    {
      if (rw == WRITE)
      {
        rw = READ;
        rwq->set_min_length(m_config->CL);
      }
      rwq->push(bk[j]->mrq);
      bk[j]->mrq->txbytes += m_config->dram_atom_size;
      CCDc = m_config->tCCD;
      bkgrp[grp]->CCDLc = m_config->tCCDL;
      RTWc = m_config->tRTW;
      bk[j]->RTPc = m_config->BL / m_config->data_command_freq_ratio;
      bkgrp[grp]->RTPLc = m_config->tRTPL;
      issued = true;
      if (bk[j]->mrq->data->get_access_type() == L2_WR_ALLOC_R)
        n_rd_L2_A++;
      else
        n_rd++;

      bwutil += m_config->BL / m_config->data_command_freq_ratio;
      bwutil_partial += m_config->BL / m_config->data_command_freq_ratio;
      bk[j]->n_access++;

#ifdef DRAM_VERIFY
      PRINT_CYCLE = 1;
      printf("\tRD  Bk:%d Row:%03x Col:%03x \n", j, bk[j]->curr_row,
             bk[j]->mrq->col + bk[j]->mrq->txbytes - m_config->dram_atom_size);
#endif
      // transfer done
      if (!(bk[j]->mrq->txbytes < bk[j]->mrq->nbytes))
      {
        bk[j]->mrq = NULL;
      }
    }
    else
      // correct row activated for a WRITE
      if (!issued && !CCDc && !bk[j]->RCDWRc && !(bkgrp[grp]->CCDLc) &&
          (bk[j]->curr_row == bk[j]->mrq->row) && (bk[j]->mrq->rw == WRITE) &&
          (RTWc == 0) && (bk[j]->state == BANK_ACTIVE) && !rwq->full())
      {
        if (rw == READ)
        {
          rw = WRITE;
          rwq->set_min_length(m_config->WL);
        }
        rwq->push(bk[j]->mrq);

        bk[j]->mrq->txbytes += m_config->dram_atom_size;
        CCDc = m_config->tCCD;
        bkgrp[grp]->CCDLc = m_config->tCCDL;
        WTRc = m_config->tWTR;
        bk[j]->WTPc = m_config->tWTP;
        issued = true;

        if (bk[j]->mrq->data->get_access_type() == L2_WRBK_ACC)
          n_wr_WB++;
        else
          n_wr++;
        bwutil += m_config->BL / m_config->data_command_freq_ratio;
        bwutil_partial += m_config->BL / m_config->data_command_freq_ratio;
#ifdef DRAM_VERIFY
        PRINT_CYCLE = 1;
        printf("\tWR  Bk:%d Row:%03x Col:%03x \n", j, bk[j]->curr_row,
               bk[j]->mrq->col + bk[j]->mrq->txbytes - m_config->dram_atom_size);
#endif
        // transfer done
        if (!(bk[j]->mrq->txbytes < bk[j]->mrq->nbytes))
        {
          bk[j]->mrq = NULL;
        }
      }
  }

  return issued;
}

bool Dram::issue_row_command(int j)
{
  bool issued = false;
  uint32_t grp = get_bankgrp_number(j);
  if (bk[j]->mrq)
  { // if currently servicing a memory request
    bk[j]->mrq->data->set_status(
        IN_PARTITION_DRAM, m_simulator->simulator_sim_cycle);
    //     bank is idle
    // else
    if (!issued && !RRDc && (bk[j]->state == BANK_IDLE) && !bk[j]->RPc &&
        !bk[j]->RCc)
    { //
#ifdef DRAM_VERIFY
      PRINT_CYCLE = 1;
      printf("\tACT BK:%d NewRow:%03x From:%03x \n", j, bk[j]->mrq->row,
             bk[j]->curr_row);
#endif
      // activate the row with current memory request
      bk[j]->curr_row = bk[j]->mrq->row;
      bk[j]->state = BANK_ACTIVE;
      RRDc = m_config->tRRD;
      bk[j]->RCDc = m_config->tRCD;
      bk[j]->RCDWRc = m_config->tRCDWR;
      bk[j]->RASc = m_config->tRAS;
      bk[j]->RCc = m_config->tRC;
      prio = (j + 1) % m_config->nbk;
      issued = true;
      n_act_partial++;
      n_act++;
    }

    else
      // different row activated
      if ((!issued) && (bk[j]->curr_row != bk[j]->mrq->row) &&
          (bk[j]->state == BANK_ACTIVE) &&
          (!bk[j]->RASc && !bk[j]->WTPc && !bk[j]->RTPc &&
           !bkgrp[grp]->RTPLc))
      {
        // make the bank idle again
        bk[j]->state = BANK_IDLE;
        bk[j]->RPc = m_config->tRP;
        prio = (j + 1) % m_config->nbk;
        issued = true;
        n_pre++;
        n_pre_partial++;
#ifdef DRAM_VERIFY
        PRINT_CYCLE = 1;
        printf("\tPRE BK:%d Row:%03x \n", j, bk[j]->curr_row);
#endif
      }
  }
  return issued;
}

// if mrq is being serviced by dram, gets popped after CL latency fulfilled
class MemFetch *Dram::return_queue_pop()
{
  return returnq->pop();
}

class MemFetch *Dram::return_queue_top()
{
  return returnq->top();
}

void Dram::set_dram_power_stats(uint32_t &cmd, uint32_t &activity,
                                uint32_t &nop, uint32_t &act, uint32_t &pre,
                                uint32_t &rd, uint32_t &wr,
                                uint32_t &req) const
{
  // Point power performance counters to low-level DRAM counters
  cmd = n_cmd;
  activity = n_activity;
  nop = n_nop;
  act = n_act;
  pre = n_pre;
  rd = n_rd;
  wr = n_wr;
  req = n_req;
}

uint32_t Dram::get_bankgrp_number(uint32_t i)
{
  if (m_config->dram_bnkgrp_indexing_policy == HIGHER_BITS)
  { // higher bits
    return i >> m_config->bk_tag_length;
  }
  else if (m_config->dram_bnkgrp_indexing_policy ==
           LOWER_BITS)
  { // lower bits
    return i & ((m_config->nbkgrp - 1));
  }
  else
  {
    assert(1);
  }
}
